﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Telerik.Web.Mvc;
using TelerikMvcGridCustomBindingHelper.Aggregates;
using TelerikMvcGridCustomBindingHelper.Projections;
using System.Xml.Serialization;

namespace TelerikMvcGridCustomBindingHelper.Util
{
    public static class Extencions
    {
        public static bool IsNotNullOrWhiteSpace(this string str)
        {
            return (string.IsNullOrWhiteSpace(str) && string.IsNullOrEmpty(str)) == false;
        }

        public static bool IsNullOrWhiteSpace(this string str)
        {
            return (string.IsNullOrWhiteSpace(str) && string.IsNullOrEmpty(str));
        }

        public static string GetPropertyPath(this Expression expression)
        {
            var hashSet = GetAllPropertiesPaths(expression) ?? new HashSet<string>();
            return hashSet.Any() ? hashSet.ElementAt(0) : null;
        }

        public static HashSet<string> GetAllPropertiesPaths(this Expression expression)
        {
            if (expression == null)
                return null;

            var visitor = new PropertyPathVisitor();
            visitor.Visit(expression);
            return visitor.Properties;
        }

        public static AggregatesContainer Push(this Dictionary<string, AggregatesContainer> dictionary, string key, object value, AggregateMethod aggregateMethod)
        {
            if (dictionary.ContainsKey(key))
            {
                dictionary[key].AddOrSet(aggregateMethod, value);
            }
            else
            {
                dictionary[key] = new AggregatesContainer().AddOrSet(aggregateMethod, value);
            }

            return dictionary[key];
        }

        public static string GetStringValue(this Enum @enum)
        {
            return EnumUtils.GetStringValue(@enum);
        }

        public static bool NotEquals(this string src, string other)
        {
            return src.Equals(other) == false;
        }

        public static IProjectionExpression<TSource> Project<TSource>(this IQueryable<TSource> source)
        {
            return new ProjectionExpression<TSource>(source);
        }

        /// <summary>
        /// Returns all distinct elements of the given source, where "distinctness"
        /// is determined via a projection and the specified comparer for the projected type.
        /// </summary>
        /// <remarks>
        /// This operator uses deferred execution and streams the results, although
        /// a set of already-seen keys is retained. If a key is seen multiple times,
        /// only the first element with that key is returned.
        /// </remarks>
        /// <typeparam name="TSource">Type of the source sequence</typeparam>
        /// <typeparam name="TKey">Type of the projected element</typeparam>
        /// <param name="source">Source sequence</param>
        /// <param name="keySelector">Projection for determining "distinctness"</param>
        /// <param name="comparer">The equality comparer to use to determine whether or not keys are equal.
        /// If null, the default equality comparer for <c>TSource</c> is used.</param>
        /// <returns>A sequence consisting of distinct elements from the source sequence,
        /// comparing them by the specified key projection.</returns>
        public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer = null)
        {
            Guard.IsNotNull(() => source);
            Guard.IsNotNull(() => keySelector);
            var knownKeys = new HashSet<TKey>(comparer);
            return source.Where(element => knownKeys.Add(keySelector(element)));
        }

        /// <summary>
        /// Helper method to xml serialize in memory.
        /// </summary>
        public static string ToXml(this List<FilterDescriptor> objToXml)
        {
            StreamWriter stWriter = null;
            string buffer;
            try
            {
                var xmlSerializer = new XmlSerializer(objToXml.GetType());
                var memStream = new MemoryStream();
                stWriter = new StreamWriter(memStream);
                xmlSerializer.Serialize(stWriter, objToXml);
                buffer = Encoding.ASCII.GetString(memStream.GetBuffer());
            }
            finally
            {
                if (stWriter != null) stWriter.Close();
            }
            return buffer;
        }

        /// <summary>
        /// returns deserialized object from the provided string
        /// </summary>
        public static T FromXml<T>(this string source)
        {
            var xmlSerializer = new XmlSerializer(typeof(T));
            T result;

            using (var sReader = new StringReader(source))
            {
                result = (T)xmlSerializer.Deserialize(sReader);
            }

            return result;
        }
    }
}